package com.gobang.modules.message;

import com.gobang.proto.GameProto;
import com.gobang.proto.GameProto.GameMessage;
import com.gobang.proto.GameProto.MessageType;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 消息分发器
 * 负责扫描所有带@MsgHandler注解的方法并注册，然后根据消息类型分发到对应的处理方法
 */
@Slf4j
@Component
public class MsgDispatcher implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    // 消息类型 -> 处理器映射
    private final Map<MessageType, HandlerInfo> handlerMap = new HashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 延迟初始化：第一次调用dispatch时才扫描所有带@MsgHandler注解的方法
     */
    public void init() {

        log.info("开始扫描消息处理器...");

        // 获取所有Bean
        String[] beanNames = applicationContext.getBeanDefinitionNames();

        for (String beanName : beanNames) {
            try {
                Object bean = applicationContext.getBean(beanName);
                Class<?> clazz = bean.getClass();

                // 扫描该Bean的所有方法
                for (Method method : clazz.getDeclaredMethods()) {
                    MsgHandler annotation = method.getAnnotation(MsgHandler.class);
                    if (annotation != null) {
                        MessageType msgType = annotation.value();

                        // 验证方法签名
                        if (!validateMethodSignature(method)) {
                            log.error("方法签名不正确: {}.{}, 应为: (ChannelHandlerContext, MessageRequest)",
                                    clazz.getName(), method.getName());
                            continue;
                        }

                        // 注册处理器
                        HandlerInfo handlerInfo = new HandlerInfo(bean, method, getRequestClass(msgType));
                        handlerMap.put(msgType, handlerInfo);

                        log.info("注册消息处理器: {} -> {}.{}",
                                msgType, clazz.getSimpleName(), method.getName());
                    }
                }
            } catch (Exception e) {
                log.warn("扫描Bean失败: {}, 错误: {}", beanName, e.getMessage());
            }
        }

        log.info("消息处理器扫描完成，共注册 {} 个处理器", handlerMap.size());
    }

    /**
     * 分发消息到对应的处理器
     */
    public void dispatch(ChannelHandlerContext ctx, GameMessage gameMessage) {
        MessageType msgType = gameMessage.getType();
        HandlerInfo handlerInfo = handlerMap.get(msgType);

        if (handlerInfo == null) {
            log.warn("未找到消息处理器: {}", msgType);
            return;
        }

        try {
            // 解析消息数据
            Message request = parseMessage(gameMessage.getData().toByteArray(), handlerInfo.requestClass);

            // 调用处理方法
            handlerInfo.method.invoke(handlerInfo.bean, ctx, request);

        } catch (InvalidProtocolBufferException e) {
            log.error("解析消息失败: type={}", msgType, e);
        } catch (Exception e) {
            log.error("处理消息失败: type={}", msgType, e);
            throw new RuntimeException("消息处理失败", e);
        }
    }

    /**
     * 验证方法签名是否正确
     */
    private boolean validateMethodSignature(Method method) {
        Class<?>[] paramTypes = method.getParameterTypes();
        return paramTypes.length == 2
                && paramTypes[0] == ChannelHandlerContext.class
                && Message.class.isAssignableFrom(paramTypes[1]);
    }

    /**
     * 根据消息类型获取对应的请求类
     */
    private Class<? extends Message> getRequestClass(MessageType msgType) {
        try {
            switch (msgType) {
                case LOGIN_REQUEST:
                    return GameProto.LoginRequest.class;
                case MATCH_REQUEST:
                    return GameProto.MatchRequest.class;
                case CANCEL_MATCH:
                    return GameProto.CancelMatchRequest.class;
                case PLACE_PIECE:
                    return GameProto.PlacePieceRequest.class;
                case SURRENDER:
                    return GameProto.SurrenderRequest.class;
                case HEARTBEAT:
                    return GameProto.HeartbeatRequest.class;
                default:
                    throw new IllegalArgumentException("未知的消息类型: " + msgType);
            }
        } catch (Exception e) {
            log.error("获取请求类失败: {}", msgType, e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析消息
     */
    private Message parseMessage(byte[] data, Class<? extends Message> clazz) throws InvalidProtocolBufferException {
        try {
            Method parseMethod = clazz.getMethod("parseFrom", byte[].class);
            return (Message) parseMethod.invoke(null, data);
        } catch (Exception e) {
            throw new InvalidProtocolBufferException("解析消息失败: " + clazz.getName());
        }
    }

    /**
     * 处理器信息
     */
    private static class HandlerInfo {
        final Object bean;
        final Method method;
        final Class<? extends Message> requestClass;

        HandlerInfo(Object bean, Method method, Class<? extends Message> requestClass) {
            this.bean = bean;
            this.method = method;
            this.requestClass = requestClass;
            this.method.setAccessible(true);
        }
    }
}
